package asia.kalin.service.impl;

import asia.kalin.config.SnowflakeConfig;
import asia.kalin.domain.dto.UpdatePassWordDTO;
import asia.kalin.domain.dto.UserInfoDTO;
import asia.kalin.domain.po.Department;
import asia.kalin.domain.po.UserInfo;
import asia.kalin.domain.vo.UserInfoVO;
import asia.kalin.mapper.DepartmentMapper;
import asia.kalin.mapper.UserInfoMapper;
import asia.kalin.service.IUserInfoService;
import asia.kalin.utils.JwtUtil;
import asia.kalin.domain.ao.Result;
import asia.kalin.utils.ThreadLocalUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Kalin
 * @since 2025-03-10
 */
@Service
@RequiredArgsConstructor
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements IUserInfoService {

    private final SnowflakeConfig snowflakeConfig;

    private final UserInfoMapper userInfoMapper;

    private final DepartmentMapper departmentMapper;

    /**
     * 用户注册
     * @param userInfoDTO
     * @return Result
     */
    @Override
    @Transactional
    public Result register(UserInfoDTO userInfoDTO) {
        // 检查用户信息合法性
        Result result = checkUserInfo(userInfoDTO,true);
        if(result != null)
            return result;

        // 判断用户是否存在
        QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
        userInfoQueryWrapper.eq("username",userInfoDTO.getUsername());
        UserInfo user = userInfoMapper.selectOne(userInfoQueryWrapper);
        if(user != null){
            return new Result<>(1,"用户已存在",null);
        }

        // 通过检查 插入数据库
        // 转换类型
        UserInfo userInfo = BeanUtil.toBean(userInfoDTO, UserInfo.class);
        // 加密密码
        String hashpw = BCrypt.hashpw(userInfo.getPassword(),BCrypt.gensalt());
        userInfo.setPassword(hashpw);
        // 生成唯一id
        Snowflake snowflake = snowflakeConfig.snowflake();
        String userid = String.valueOf(snowflake.nextId());
        userInfo.setUserId(userid);
        // 设置修改创建时间
        userInfo.setCreateTime(LocalDateTime.now());
        userInfo.setUpdateTime(LocalDateTime.now());
        userInfoMapper.insert(userInfo);

        return new Result<>(0,"注册成功",null);
    }

    /**
     * 用户登录
     * @param userInfoDTO
     * @return Result
     */
    @Override
    public Result login(UserInfoDTO userInfoDTO) {
        // 检查用户名密码
        if(userInfoDTO.getUsername() == "" || userInfoDTO.getPassword() == ""){
            return new Result<>(1,"用户名密码不能为空",null);
        }
        if(userInfoDTO.getUsername().length() < 2 || userInfoDTO.getPassword().length() < 8){
            return new Result<>(1,"用户名密码长度不够",null);
        }

        // 检查用户是否注册
        QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
        userInfoQueryWrapper.eq("username",userInfoDTO.getUsername());
        UserInfo userInfo = userInfoMapper.selectOne(userInfoQueryWrapper);
        if(userInfo == null){
            return new Result<>(1,"用户未注册",null);
        }

        // 匹配密码是否正确
        if(!BCrypt.checkpw(userInfoDTO.getPassword(),userInfo.getPassword())){
            return new Result<>(1,"用户名密码错误",null);
        }

        Map<String,Object> claims = new HashMap<>();
        claims.put("userId",userInfo.getUserId());
        claims.put("departmentId",userInfo.getDepartmentId());
        String token = JwtUtil.genToken(claims);

        return new Result<>(0,"登录成功",token);
    }

    /**
     * 修改用户信息
     * @param userInfoDTO
     * @return Result
     */
    @Override
    @Transactional
    public Result userUpdateInfo(UserInfoDTO userInfoDTO) {
        // 检查用户信息合法性
        Result result = checkUserInfo(userInfoDTO,false);
        if(result != null)
            return result;

        // 拿到当前用户的id
        Map<String,Object> claims = ThreadLocalUtil.get();
        String userId = (String) claims.get("userId");

        // 转换成UserInfo
        UserInfo userInfo = BeanUtil.toBean(userInfoDTO, UserInfo.class);
        userInfo.setUpdateTime(LocalDateTime.now());
        userInfo.setUserId(userId);
        // 修改用户信息
        int i = baseMapper.updateById(userInfo);
        if(i <= 0){
            return new Result<>(1,"修改失败",null);
        }

        return new Result<>(0,"修改成功",null);
    }

    /**
     * 修改用户密码
     * @param updatePassWordDTO
     * @return Result
     */
    @Override
    @Transactional
    public Result userUpdatePW(UpdatePassWordDTO updatePassWordDTO) {
        // 拿到当前用户id
        Map<String,Object> claims = ThreadLocalUtil.get();
        String userId = (String) claims.get("userId");

        // 查询当前用户信息
        QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
        userInfoQueryWrapper.eq("user_id",userId);
        UserInfo userInfo = baseMapper.selectOne(userInfoQueryWrapper);

        // 比对密码
        if(!BCrypt.checkpw(updatePassWordDTO.getOldPassWord(),userInfo.getPassword())) {
            return new Result<>(1, "与原密码不符", null);
        }
        if(!updatePassWordDTO.getNewPassWord().equals(updatePassWordDTO.getRePassWord())){
            return new Result<>(1,"两次密码不符",null);
        }
        if(BCrypt.checkpw(updatePassWordDTO.getNewPassWord(),userInfo.getPassword())){
            return new Result<>(1, "与原密码相同", null);
        }

        // 修改密码
        // 加密
        String newPassWord = BCrypt.hashpw(updatePassWordDTO.getNewPassWord());
        userInfo.setPassword(newPassWord);
        int i = baseMapper.updateById(userInfo);
        if(i <= 0){
            return new Result<>(1,"修改失败",null);
        }

        // 删除本地存储的ThreadLocalUtil
        ThreadLocalUtil.remove();

        return new Result<>(0,"修改成功",null);
    }

    @Override
    public Result getUserInfo() {
        // 拿到当前用户id
        Map<String,Object> claims = ThreadLocalUtil.get();
        String userId = (String) claims.get("userId");

        UserInfo userInfo = baseMapper.selectById(userId);
        if(userInfo == null){
            return new Result<>(1,"用户不存在",null);
        }
        UserInfoVO userInfoVO = BeanUtil.toBean(userInfo, UserInfoVO.class);
        return new Result<>(0,"查询成功",userInfoVO);
    }

    /**
     * 检查手机号是否符合规则
     * @param phoneNumber
     * @return boolean
     */
    public boolean checkPhone(String phoneNumber){
        // 正则校验手机号码的格式
        String regex = "^(13[0-9]|14[5-9]|15[0-3,5-9]|16[2,5,6,7]|17[0-8]|18[0-9]|19[1,3,5-9])\\d{8}$";
        return Pattern.matches(regex, phoneNumber);
    }

    /**
     * 校验UserInfoDTO合法性
     * @param userInfoDTO
     * @return
     */
    public Result checkUserInfo(UserInfoDTO userInfoDTO,boolean isCheckPassword){
        // 用户名 密码 手机号不能为空
        if((userInfoDTO.getPassword() == "" && isCheckPassword) || userInfoDTO.getUsername() == "" || userInfoDTO.getPhoneNumber() == ""){
            return new Result<>(1,"信息不能为空",null);
        }

        // 用户名 密码 手机号符合长度
        if(userInfoDTO.getUsername().length() < 2 || userInfoDTO.getPassword() != null && userInfoDTO.getPassword().length() < 8 || userInfoDTO.getPhoneNumber().length() != 11){
            return new Result<>(1,"用户名、密码或者手机号不符合长度",null);
        }

        // 检查用户手机号 用户密码是否合法
        if(!checkPhone(userInfoDTO.getPhoneNumber())){
            return new Result<>(1,"手机号格式不对",null);
        }

        // 查询部门id是否存在
        List<Department> departments = departmentMapper.selectList(null);
        boolean flag = false;
        if(departments != null){
            for(Department d:departments){
                if(d.getDepartmentId().equals(userInfoDTO.getDepartmentId())){
                    flag = true;
                    break;
                }
            }
        }
        if(!flag){
            return new Result<>(1,"部门不存在",null);
        }

        return null;
    }
}
